#ifndef __PROTOCAL__
#define __PROTOCAL__

#include <sys/types.h>
#include <sys/stat.h>
#include <iostream>
#include <sys/wait.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <string>
#include <cstring>
#include <unordered_map>
#include "Log.hpp"
#include <vector>
#include <algorithm>
#include <mutex>
#include <sys/types.h>
#include <fcntl.h>
#include "TcpSocket.hpp"
#include <sstream>
#include "Util.hpp"

// static std::mutex _mutex;

bool STOP = false;

#define OK 200
#define NO_FIND 404

#define WEBROOT "/webroot"
#define HOME_PAGE "/home_page.html"
#define LINE_END "\r\n"
#define HTML404 "/404.html"

// 匹配状态码解释
static std::string CodeCommit(int code)
{
    std::string tmp;
    switch (code)
    {
    case 404:
        tmp = "资源未找到";
        break;

    case 200:
        tmp = "OK";
        break;

    default:
        break;
    }
    return tmp;
}

// 响应协议
class HttpResponse
{
public:
    std::string Respone_Line;             // 响应行
    std::vector<std::string> hander_list; // 响应报头集合
    // std::string Respone_header; // 响应报头
    std::string Respone_Blank; // 响应空行
    std::string Respone_body;  // 响应正文

    int status; // 响应状态码, 1: 已响应；0：未响应

    int code = OK;           // 响应 描述符
    unsigned int size;  // 打开文件大小字节数 ？校验和吧
    std::string suffix; // 资源后缀名
    
public:
    HttpResponse()
        :size(0)
    {
    }

    ~HttpResponse()
    {
    }
};

// 请求协议
class HttpWork
{
public:
    // 报文四部分
    std::string line;                        // 请求首行
    std::vector<std::string> Request_header; // 请求报头 ————暂时设置string,具体请求参数暂定
    std::string Blank;                       // 请求选项
    std::string request_body;                // 请求主体
    // 首行
    std::string method; // 请求方式
    std::string url;    // 请求网址
    std::string verson; // 版本号
    // 解析报头
    std::unordered_map<std::string, std::string> header_kv; // 请求报头，参数设置
    ssize_t content_length;                                 // 正文长度
    // 对网址进行解析
    std::string address;       // 请求路径
    std::string parameter; // 请求参数

    bool cgi; // CGI处理标识符
    size_t _socket;

public:
    HttpResponse httpresponse;

public:
    HttpWork(size_t socket)
        : content_length(0), cgi(false), _socket(socket)
    {
        // 取掉行 
        // 取掉首部
        // RecvHttpRequestLine();
        // RecvHttpRequestHander();
        if (RecvHttpRequestLine() == -1 ||
        RecvHttpRequestHander() == -1)
        {
             STOP = true;
             Logmessage(WARN, "Rec fail");
        }
    }

    ~HttpWork()
    {
    }

public:
    // 接收首行
    int RecvHttpRequestLine()
    {
        if (Util::Readline(_socket, line) == -1)
            return -1;
        line.resize(line.size() - 1);
        Logmessage(INFO, "Recv Line success");
        return 0;
    }
    // 接收报文头部
    int RecvHttpRequestHander()
    {
        int set = 0;
        std::string head_str;
        while (1)
        {
            head_str.clear();
            if (Util::Readline(_socket, head_str) == -1)
                return -1;

            if (head_str == "\n")
                break;
            head_str.resize(head_str.size() - 1);
            Request_header.push_back(head_str);
        }
        Logmessage(INFO, "Recv Hander success");
        return 0;
    }

    // 接收正文
    int RecvHttpRequestBody()
    {
        int length = content_length;
        while (length)
        {
            char x = 'g';
            int bx =  recv(_socket, &x, 1, 0);
            if (bx == -1)
                std::cerr << strerror(errno) << std::endl;

            request_body.push_back(x);
            length--;
        }
        Logmessage(INFO, "recv body success : %s", request_body.c_str());
        return 0;
    }
    // 分析请求报文
    int PaseHttpRequest()
    {
        PaseHRline();
        PaseHRHeader();
    }

    // 解析URL参数，有参数返回0，否则-1
    int PaseURL()
    {
        // URL中路径与参数的分割符为"?"
        return Util::Stringcut(url, address, parameter, "?");
    }

    int PaseHRline()
    {
        std::stringstream ss(line);
        ss >> method >> url >> verson;
        std::transform(method.begin(), method.end(), method.begin(), ::towupper);
        Logmessage(INFO, "method:%s", method.c_str());
        Logmessage(INFO, "url:%s", url.c_str());
        Logmessage(INFO, "verson:%s", verson.c_str());
        return 0;
    }

    int PaseHRHeader()
    {
        for (auto &n : Request_header)
        {
            std::string key;
            std::string value;
            Util::Stringcut(n, key, value);
            header_kv.insert({key, value});
            // Logmessage(INFO, "%s: %s", key.c_str(), value.c_str());
        }

        if (method == "POST");
        content_length = atoi(header_kv["Content-Length"].c_str());
    }

    bool IsGet()
    {
        if (method == "GET")
            return 0;
        return -1;
    }

public:
    int BuildHttpRespose()
    {
        // 1.检测请求中信息，并检测期中错误，选项
        if (method != "GET" && method != "POST")
        {
            httpresponse.code = NO_FIND;
        }
        // 2. 判断请求方式，GET读取参数，POST则读取正文再进行其他处理
        if (method == "GET" && parameter.size() != 0)
        {
            cgi = true;
        }
        else if (method == "POST")
        {
            cgi = true;
        }
        else 
        {
            Logmessage(INFO, "just GET");
        }

        if (address == "/favicon.ico")
            address = "/";

        // 3.对请求资源进行分析
        std::string tmp = WEBROOT;
        tmp += address;
        address = tmp;
        Logmessage(INFO, "URL 路径：%s", address.c_str());
        Logmessage(INFO, "URL 参数：%s", parameter.c_str());
        if (address[address.size() - 1] == '/')
        {
            // 如若是没有指向哪个资源，则指向默认首页
            //  address += HOME_PAGE;
            address += "index.html"; // 暂时替代
            Logmessage(INFO, " address ：%s", address.c_str());
        }
        else
        {
            // 判断该资源是否存在
            struct stat file_stat;
            std::string tmp = ".";
            if (stat((tmp + address).c_str(), &file_stat) == 0)
            {
                // 是目录？若是则重定向一下
                if (S_ISDIR(file_stat.st_mode))
                {
                    address += HOME_PAGE;
                    stat((tmp + address).c_str(), &file_stat);
                }
                httpresponse.size = file_stat.st_size;
                // 检测是否是可执行程序
                if ((file_stat.st_mode & S_IXUSR) || (file_stat.st_mode & S_IXGRP) || (file_stat.st_mode & S_IXOTH))
                {
                    cgi = true;
                }
                else
                {
                    std::cout << "其他类型文件处理正在开发中" << std::endl;
                }
            }
            else
            {
                Logmessage(WARN, "file not find:%s", address.c_str());
                httpresponse.code = NO_FIND;
            }
        }

        // 4.对请求，进行非CGI响应
        if (!cgi && httpresponse.code == OK)
        {
            // std::cout << 1 << std::endl;
            return ProcessNoCGI();
        }
        else if (cgi && httpresponse.code == OK)
        {
            // std::cout << 2 << std::endl;
            if (method == "POST")
                RecvHttpRequestBody();
            
            ProcessCGI();
            BuildResponseLine();

            std::string tmp = "Conent-Length";
            tmp += SEQ;
            tmp += std::to_string(httpresponse.Respone_body.size());
            httpresponse.hander_list.push_back(tmp);

            return SendResponse(_socket);
        } else  // code 错误，构建404报文
        {
            // std::cout << 3 << std::endl;
            // std::cout << "cgi:" << cgi << "code:" << httpresponse.code << std::endl;
            Logmessage(WARN, "code 404");
            return Error_response(HTML404);
        }
    }

    int BuildResponseLine()
    {
        httpresponse.Respone_Line = verson;
        httpresponse.Respone_Line += " ";
        httpresponse.Respone_Line += std::to_string(httpresponse.code);
        httpresponse.Respone_Line += " ";
        httpresponse.Respone_Line += CodeCommit(httpresponse.code);
        httpresponse.Respone_Line += LINE_END;
    }

    int SendResponse(int pd)
    {
        // 响应空行
        httpresponse.Respone_Blank = LINE_END;
        std::string room;
        room += httpresponse.Respone_Line;
        for (auto &i : httpresponse.hander_list)
        {
            room += i;
            room += LINE_END;
        }
        room += httpresponse.Respone_Blank;

        if (send(_socket, room.c_str(), room.size(), 0) == -1)
        {
            STOP = 1;
            Logmessage(WARN, "send1 fail");
            return -1;
        }

        if (httpresponse.Respone_body.size() != 0)
        {   
            // std::cout << room << httpresponse.Respone_body << std::endl;
            if (send(_socket, httpresponse.Respone_body.c_str(), httpresponse.Respone_body.size(), 0) == -1)
                Logmessage(WARN, "send2 fail");
        }else 
        {
             if (sendfile(_socket, pd, nullptr, httpresponse.size) == -1)
             Logmessage(WARN, "send2 fail");
        }
        Logmessage(INFO, "已响应");
    }

    // 针对主要是主要是"GET"非参数传递的CGI响应
    int ProcessNoCGI()
    {
        std::string ptr = ".";
        ptr += address;
        int pd = open(ptr.c_str(), O_RDONLY);
        if (pd == -1)
        {
            Logmessage(ERROR, "resource file open fail :", ptr.c_str());
            return -1;
        }

        // 响应行
        BuildResponseLine();

        // 响应报头，这里只做简单的 content-type content-length
        
        //打开资源文件信息，并记录资源大小,也可以使用fstat
        struct stat file;
        stat(ptr.c_str(), &file);
        httpresponse.size = file.st_size;
        httpresponse.suffix = address.substr(address.rfind(".") + 1);

        std::string tmp = "Content-Length";
        tmp += SEQ;
        tmp += std::to_string(httpresponse.size);
        httpresponse.hander_list.push_back(tmp);

        tmp = httpresponse.suffix;
        tmp += SEQ;
        tmp += Content_type_transform(httpresponse.suffix);
        httpresponse.hander_list.push_back(tmp);

        // 响应
        return SendResponse(pd);
    }

    // 启用CGI
    int ProcessCGI()
    {
        std::string tmp_iurlb = ".";
        tmp_iurlb += address;
        int output[2]; // 线程发数据
        int inget[2]; // 线程接收结果
        pipe(output);
        pipe(inget);
        pid_t pd = fork();
        // 线程为主视角
        if (pd == 0)
        {
            // 子
            close(output[1]);
            close(inget[0]);
            // 1.接收数据,准备程序替换

            std::string room = "METHOD=";   //请求类型环境变量
            room += method;
            putenv((char *)room.c_str());

            std::string tmp;          // get参数 环境变量
            std::string room_length;  // post正文的长度环境变量

            if (method == "POST")
            {
                room_length = "CONTENT-LENHTH=";
                room_length += std::to_string(content_length);
                putenv((char *)room_length.c_str());
            }
            else if (method == "GET" && parameter.size() != 0)
            {
                tmp = "PARAMENTER=";
                tmp += parameter;
                putenv((char *)tmp.c_str());
            }
            // std::cerr << "debug : excel:" << address.c_str() <<  std::endl;
           
            // 约定：子进程只需从标准输入输出进行获取数据
            dup2(output[0], 0);
            dup2(inget[1], 1);

            execl(("." + address).c_str(), nullptr); // 疑问：既然通过环境变量来传参数，那buff到时候传过去的就是
            close(0);
            close(1);
            std::cerr << "GET EXECL FAIL:" << std::endl;
            return -1;
        }
        else if (pd > 0)
        {
            // 父
            close(output[0]);
            close(inget[1]);

            if (method == "POST");
            {
                const char* str = request_body.c_str();
                int total = 0;
                int size = 0;                
                while ((total <= content_length) && 
                 (size = write(output[1], str + total, request_body.size() - total) > 0))
                 {
                    total += size;
                 }
            }
            
            //接收CGI 返回值
            while (1)
            {
                 char x = 'g';
                 int set =  read(inget[0], &x, 1);
                 if ( x == '\n')
                    break;
                httpresponse.Respone_body.push_back(x);
            }
            // std::cout << "res CGI:" << httpresponse.Respone_body << std::endl;
            int status = 0;
            waitpid(pd, &status, 0); // 线程阻塞试等待
            if (WIFEXITED(status) == 0)
                Logmessage(WARN, "CGI exit with error code");

            close(output[1]);
            close(inget[0]);
            
            return 0;
        }
        else
        {
            Logmessage(ERROR, "fork fail");
            return -1;
        }
    }
    
    //404响应, 方法：1.修改资源路径，及部分信息，就当访问404 2.通过重新构建错误的响应行，头部，正文，发送
    int Error_response(const std::string& error_address)
    {
        std::string change_url = WEBROOT;
        change_url += error_address;

        address = change_url;
        httpresponse.suffix = address.substr(address.rfind(".") + 1);
        return ProcessNoCGI();
    }
};
#endif
